home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / PCCardNetworkSample / EnetDrvrSrc.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  21.1 KB  |  742 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        EnetDrvrSrc.c
  3.  
  4.     Contains:    A sample Ethernet driver source sample based on the sample loopback driver
  5.                 which is supplied as part of the OT Module Driver SDK.  This source file 
  6.                 extends the loopback driver to demonstrate 
  7.                 1. the installation of the interrupt handler,
  8.                 2. how to access the kAAPLDeviceLogicalAddress property in order to obtain 
  9.                     the base address registers for the PC Card.
  10.                 3. how to read the Ethernet burned in address (BIA) from either
  11.                     attribute memory, if not in the CIS, or from the CISTPL_FUNCE if it is
  12.                     in the CIS.
  13.                     
  14.     Original comments:     
  15.                  * Simple loopback driver utilizing Mentat DLPI Template Code (dlpiether.c)
  16.                  * This file, combined with dlpiether.c (or linked against the Shared LIbrary
  17.                  * containing dlpiether.c) is a functioning loopback driver for Open Transport.
  18.                  * This file also serves as a template for writing the hardware-specific
  19.                  * code for a driver which will use dlpiether.c.  Comments in the code and
  20.                  * the accompanying documentation, Mentat DLPI Driver Template, provide
  21.                  * necessary information.
  22.  
  23.     Written by: Mentat Inc.    
  24.  
  25.     Change History (most recent first):
  26.                 8/16/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  27.                 
  28.  
  29. */
  30. char    sccs_loop_id[] = "@(#)EnetDrvrSrc.c    1.2";
  31.  
  32. /*
  33.  * "BoardX:" in comments refers to a typical hardware driver.  These are hints
  34.  * and suggestions for converting this code to an actual hardware driver.
  35.  */
  36. #include <OpenTptModule.h>
  37. #include <dlpi.h>
  38. #include "Devices.h"
  39. #include <NameRegistry.h>
  40. #include <DriverServices.h>
  41. #include <Interrupts.h>
  42. #include <OpenTptLinks.h>
  43. #include <OpenTptPCISupport.h>
  44. #include "dlpiuser.h"
  45. #include "dlpiether.h"
  46. #define ulong UInt32
  47. #define uint UInt16
  48.  
  49. #include <PCCard.h>
  50. #include <PCCardTuples.h>
  51.  
  52. // enter the name of the module as registered using the OTRegisterPort call.
  53. // Use the PCCardDisplayNameRegistry utility to find the module name for the
  54. // This name MUST match else TCP/IP services WILL not work.
  55. #define kEnetCardName    "myEnetDriverName"
  56.  
  57. /*
  58.  * The following defines are inserted here for convenience for the 
  59.  * loopback driver.  A "real" driver may place these in separate header
  60.  * files and/or use system-defined values.
  61.  */
  62. //#define LOOPBACK_DRIVER        /* conditionally compiles loopback code */
  63.  
  64. #define ENETADDRINATTRIBUTE        /* conditionally compiles code to obtain the ethernet
  65.                                     burned in address from attribute memory 
  66.                                     comment out this define to had code access BIA
  67.                                     from CIS
  68.                                 */
  69.  
  70. /* Some useful MacBug debugging macros */
  71.  
  72. #define mps_printf            OTKernelPrintfForMentat
  73. #define loop_error(a)        mps_printf a
  74. #define loop_trace(a)        mps_printf a
  75. #define loop_debug(a)        if(loop_debug) {mps_printf a;} else {;}
  76. #define loop_debug2(a)        if(loop_debug >= 2) {mps_printf a;} else {;}
  77. #define    trace_args    "  "        
  78. #define    trace0        "0 "    /* Fatal */
  79. #define    trace1        "1 "    /* General */
  80. #define    trace2        "2 "    /* Out of Memory */
  81. #define    trace3        "3 "    /* Event */
  82. #define    trace_eol        
  83. extern    int    OTKernelPrintfForMentat(char * fmt, ...);
  84. int loop_debug = 1;
  85.  
  86. #define    MAX_PACKET_SIZE    1500    /* We're ethernet, remember :-) */
  87. #define    MIN_PACKET_SIZE    60    /* We'll pad short packets Ethernet style */
  88. #define LOOP_HIWAT    2048    /* Flow control high water mark */
  89. #define LOOP_LOWAT    64    /* Flow control low water mark */
  90.  
  91. /* just so DL_INFO_ACK has something to report */
  92. static    unsigned char    my_hardware_addr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
  93.  
  94. /*
  95.  * See OpnTptLinks.h for more
  96.  */
  97.  
  98. /* Driver information 'per board', e.g., internal control structure. */
  99. typedef struct board_s {
  100.     dle_t    board_dle;        /* Must be first in structure. */
  101.     /* BoardX: Fields needed for controlling hardware go here */
  102. } board_t;
  103.  
  104. /* 
  105.  * STREAMS instance data (q_ptr) 
  106.  */
  107. typedef struct loop_s {
  108.     dcl_t    loop_dcl;        /* Must be first in structure */
  109.     /* BoardX: Any 'per stream' fields needed by board go here */
  110. } loop_t;
  111. #define    loop_dle    loop_dcl.dcl_hw        /* pointer to board_t structure */
  112.  
  113. static    void    loop_bcopy_to_noncached(unsigned char * src, unsigned char * dst
  114.             , unsigned int count);
  115. static    int    loop_close(queue_t * q, int flag, cred_t * credp);
  116. static    void    loop_hw_address_filter_reset(void * loopvp, dle_addr_t * addr_list
  117.             , ulong addr_count, ulong promisc_count
  118.             , ulong multi_promisc_count, ulong accept_broadcast
  119.             , ulong accept_error);
  120. static    void    loop_hw_start(void * loopvp);
  121. static    void    loop_hw_stop(void * loopvp);
  122. static    int    loop_open(queue_t * q, dev_t * devp, int flag, int sflag
  123.             , cred_t * credp);
  124. static    int    loop_rsrv(queue_t * q);
  125. static    int    loop_wput(queue_t * q, mblk_t * mp);
  126. install_info*    GetOTInstallInfo ();
  127. Boolean InitStreamModule (RegEntryID * our_id);
  128. void board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count);
  129. // DRF start: added for PCCard Kitchen
  130.  
  131. InterruptMemberNumber board_intr(InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount);
  132. void TerminateStreamModule (void);
  133. OTResult ValidateHardware (RegEntryID * our_id);
  134.  
  135. InterruptEnabler    gInterruptEnabler;
  136. InterruptDisabler    gInterruptDisabler;
  137.  
  138. volatile void *        gCardBaseAddress;
  139. unsigned char        gEthernetHardwareAddress[6];
  140.  
  141. // DRF end
  142.  
  143.  
  144. /*
  145.  * Global pointer to the board_t structure for this device instantiation.  This
  146.  * structure is allocated in the InitStreamModule routine and freed in
  147.  * TerminateStreamModule.  CFM guarantees us a separate data space for all
  148.  * instances of the device, so this pointer is always unique.
  149.  */
  150. board_t    * board_global;
  151.  
  152. /* STREAMS configuration structures. */
  153. struct module_info info =  {
  154.     kEnetModuleID, kEnetCardName
  155.     , MIN_PACKET_SIZE, MAX_PACKET_SIZE
  156.     , LOOP_HIWAT, LOOP_LOWAT
  157. };
  158.  
  159. /* 
  160.  * STREAMS qinit structures.  Drivers have no rput.
  161.  */
  162. struct qinit rinit = {
  163.     NULL, loop_rsrv, loop_open, loop_close, NULL, &info
  164. };
  165.  
  166. struct qinit winit = {
  167.     loop_wput, NULL, loop_open, loop_close, NULL, &info
  168. };
  169.  
  170. struct streamtab loopback_info = { &rinit, &winit };
  171.  
  172. /* 
  173.  * BoardX:  Hardware drivers would typically add 'kOTModUsesInterrupts' to
  174.  * flags entry of following.
  175.  */
  176.  
  177. install_info    loopback_install_info =
  178. {
  179.     &loopback_info,
  180.     kOTModIsDriver | kOTModUpperIsDLPI,
  181.     SQLVL_MODULE,
  182.     NULL,
  183.     0,
  184.     0
  185. };
  186.  
  187. void board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count)
  188. {
  189.     unsigned int iterations;
  190.  
  191.     /* Do short copies with a simple loop. */
  192.     if (count <= 15) {
  193.         do {
  194.             --count;
  195.             *dst++ = *src++;
  196.         } while (count);
  197.         return;
  198.     }
  199.  
  200.     /* Align on 8 byte dst boundary */
  201.     iterations = (8 - ((unsigned int)dst & 0x7)) & 0x7;
  202.     count -= iterations;
  203.     if (iterations) {
  204.         do {
  205.             --iterations;
  206.             *dst++ = *src++;
  207.         } while (iterations);
  208.     }
  209.  
  210.     /* Copy 32 byte chunks */
  211.     iterations = count >> 5;
  212.     count &= 0x1F;
  213.     if (iterations) {
  214.         double * dsrc = (double *)src;
  215.         double * ddst = (double *)dst;
  216.         do {
  217.             iterations--;
  218.             ddst[0] = dsrc[0];
  219.             ddst[1] = dsrc[1];
  220.             ddst[2] = dsrc[2];
  221.             ddst[3] = dsrc[3];
  222.             ddst += 4;
  223.             dsrc += 4;
  224.         } while (iterations);
  225.         src = (unsigned char *)dsrc;
  226.         dst = (unsigned char *)ddst;
  227.     }
  228.  
  229.     /* Copy 4 byte chunks */
  230.     iterations = count >> 2;
  231.     count &= 0x3;
  232.     if (iterations) {
  233.         unsigned long * lsrc = (unsigned long *)src;
  234.         unsigned long * ldst = (unsigned long *)dst;
  235.         do {
  236.             --iterations;
  237.             *ldst++ = *lsrc++;
  238.         } while (iterations);
  239.         src = (unsigned char *)lsrc;
  240.         dst = (unsigned char *)ldst;
  241.     }
  242.  
  243.     /* Copy the rest */
  244.     while (count--)
  245.         *dst++ = *src++;
  246. }
  247.  
  248. /*
  249.  * Template interrupt routine for processing inbound packets.
  250.  */
  251.  
  252. InterruptMemberNumber board_intr (InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount)
  253. {
  254.     #pragma unused(ISTmember,refCon,theIntCount)
  255.     board_t    * board = board_global;
  256.     dle_t    * dle = &board->board_dle;
  257.  
  258.     OTEnterInterrupt();
  259.  
  260.     loop_debug((trace_args "entered" trace_eol));
  261.  
  262.     OTLeaveInterrupt();
  263.     return 1;
  264. }
  265.  
  266.  
  267. /* This function must be exported; see loop.exp */
  268. install_info *
  269. GetOTInstallInfo ()
  270. {
  271.     DebugStr("\p entered GetOTInstallInfo ");
  272.     loop_debug((trace_args "GetOTInstallInfo" trace_eol));
  273.     return &loopback_install_info;
  274. }
  275.  
  276. Boolean
  277. InitStreamModule (RegEntryID * our_id)
  278. {
  279.     board_t    * board;
  280.     dle_t    * dle;
  281. // DRF start: added for PCCard Kitchen
  282.     RegPropertyValueSize    propertySize;
  283.     InterruptSetMember        our_ist;
  284.     InterruptHandler        old_interrupt_handler;
  285.     void *                    refCon;
  286.     OSStatus                status;
  287.  
  288. #ifdef    ENETADDRINATTRIBUTE
  289. //    ethernet address is in attribute memory, but not CIS
  290.     LogicalAddress            attribute_window_base;
  291.     ByteCount                attribute_window_size;
  292.     PCCardAccessSpeed        attribute_window_speed;
  293.     PCCardWindowOffset        attribute_window_offset;
  294.     PCCardWindowID            attribute_window_id;
  295. #else
  296. //    ethernet address is in CISTPL_FUNCE
  297.     PCCardTupleIterator        tupleIterator;
  298.     char                    tupleBuffer[MAX_TUPLE_SIZE];
  299.     UInt32                    tupleBufferSize;
  300.     PCCardTupleKind            foundTuple;
  301.     UInt32                    foundTupleDataSize;
  302. #endif
  303. // DRF end
  304.  
  305.     loop_debug((trace_args "InitStreamModule" trace_eol));
  306.  
  307.     /* Allocate the board_t structure for this device */
  308.     board = (board_t *)OTAllocMem(sizeof(board_t));
  309.     if (!board)
  310.         return false;
  311.  
  312.     /* These are the hardware start/stop/reset functions */
  313.     bzero((char *)board, sizeof(board_t));
  314.     dle = &board->board_dle;
  315.     dle->dle_hw.dlehw_start = loop_hw_start;
  316.     dle->dle_hw.dlehw_stop = loop_hw_stop;
  317.     dle->dle_hw.dlehw_address_filter_reset = loop_hw_address_filter_reset;
  318.     dle->dle_hw.dlehw_send_error = NULL;
  319.     dle->dle_hw.dlehw_recv_error_flags = 0;
  320.  
  321.     /*
  322.      * Suggestions:
  323.      * - Install interrupt vectors in case any memory allocations are
  324.      *    required.  Interrupts should not be enabled until the
  325.      *    board start routine is called.
  326.      * - Reset the hardware to a known state.  If the hardware does
  327.      *    not respond, then return false.
  328.      * - Read the Ethernet address from the board's ROM area.  This
  329.      *    address should be copied into both the dle_factory_addr
  330.      *    field and the dle_current_addr field.
  331.      */
  332.  
  333. // DRF start: added for PCCard Kitchen
  334.  
  335.     /* install interrupt handler */
  336.  
  337.     loop_debug((trace_args "about to install interrupt handler!" trace_eol));
  338.  
  339.     propertySize = sizeof(InterruptSetMember);
  340.     status = RegistryPropertyGet(our_id, kISTPropertyName, &our_ist, &propertySize);
  341.     if (status != noErr) {
  342.         loop_debug((trace_args "InitStreamModule install interrupt vector: can't get driver-ist property." trace_eol));
  343.         return status;
  344.     }
  345.  
  346.     status = GetInterruptFunctions(our_ist.setID, our_ist.member,
  347.                                     &refCon, &old_interrupt_handler, &gInterruptEnabler, &gInterruptDisabler );
  348.     if (status != noErr) {
  349.         loop_debug((trace_args "InitStreamModule install interrupt vector: can't get interrupt functions." trace_eol));
  350.         return status;
  351.     }
  352.  
  353.     status = InstallInterruptFunctions(our_ist.setID, our_ist.member,
  354.                                     NULL, board_intr, gInterruptEnabler, gInterruptDisabler );
  355.     if (status != noErr) {
  356.         loop_debug((trace_args "InitStreamModule install interrupt vector: can't set interrupt functions." trace_eol));
  357.         return status;
  358.     }
  359.  
  360.     /* get base address */
  361.  
  362. //    DebugStr("\p about to get base address!");
  363.  
  364.     propertySize = sizeof(gCardBaseAddress);
  365.     status = RegistryPropertyGet(our_id, kAAPLDeviceLogicalAddress, &gCardBaseAddress, &propertySize);
  366.     if (status != noErr) {
  367.         loop_debug((trace_args "InitStreamModule install interrupt vector: can't get AAPL,address property." trace_eol));
  368.         return status;
  369.     }
  370.  
  371.  
  372. //    Get Ethernet Hardware Address
  373.  
  374. #ifdef    ENETADDRINATTRIBUTE
  375. //    If your card has ethernet hardware address in attribute memory, but NOT in a tuple, then use the following:
  376.  
  377.         /* IMPORTANT NOTE */
  378.         // the following values are for compilation purposes only.  When accessing the ethernet address
  379.         // from attribute memory, the following values must be set as appropriate for your card.
  380.         
  381.     attribute_window_size = 4096;
  382.     attribute_window_speed = 600;
  383.     attribute_window_offset = 0;
  384.     status = PCCardRequestWindow(our_id, kPCCardAttributeMemorySpace, &attribute_window_base, &attribute_window_size,
  385.                             &attribute_window_speed, &attribute_window_offset, &attribute_window_id);
  386.     if (status != noErr) {
  387.         loop_debug((trace_args "InitStreamModule get ethernet address: can't request attribute window" trace_eol));
  388.         return status;
  389.     }
  390.  
  391.     status = PCCardReleaseWindow(attribute_window_id);
  392.  
  393.         // save the Ethernet BIA into the dle structure as specified in the DLPI template note
  394.     bcopy(dle->dle_factory_addr, attribute_window_base, 6);
  395.     bcopy(dle->dle_current_addr, attribute_window_base, 6);
  396.  
  397. #else
  398. //    if your card has ethernet hardware address in CISTPL_FUNCE, it is much easier
  399.  
  400.     tupleIterator = PCCardNewTupleIterator();
  401.     if (tupleIterator == NULL)    {
  402.         loop_debug((trace_args "InitStreamModule get ethernet address: can't make tuple iterator" trace_eol));
  403.         return status;
  404.     }
  405.  
  406.     tupleBufferSize = sizeof(tupleBuffer);
  407.     status = PCCardGetFirstTuple(our_id, CISTPL_FUNCE, tupleIterator, &tupleBuffer[0], &tupleBufferSize,
  408.                                 &foundTuple, &foundTupleDataSize);    
  409.  
  410.     while (status == noErr)    {
  411.         // we found a CISTPL_FUNCE tuple
  412.         
  413.         DebugStr("\pFound CISTPL_FUNCE");        //    for RATOC card, the address is stored starting at the 5th byte
  414.         
  415.         if (1)    {
  416.             my_hardware_addr[0] = tupleBuffer[5];
  417.             my_hardware_addr[1] = tupleBuffer[6];
  418.             my_hardware_addr[2] = tupleBuffer[7];
  419.             my_hardware_addr[3] = tupleBuffer[8];
  420.             my_hardware_addr[4] = tupleBuffer[9];
  421.             my_hardware_addr[5] = tupleBuffer[10];
  422.  
  423.             DebugStr("\pgot hardware address");
  424.     
  425.             break;
  426.         }
  427.                 
  428.         tupleBufferSize = sizeof(tupleBuffer);
  429.         status = PCCardGetNextTuple(our_id, CISTPL_FUNCE, tupleIterator, &tupleBuffer[0], &tupleBufferSize,
  430.                                 &foundTuple, &foundTupleDataSize);    
  431.     }
  432.  
  433.     PCCardDisposeTupleIterator(tupleIterator);
  434.  
  435.         // save the Ethernet BIA into the dle structure as specified in the DLPI template note
  436.     bcopy(dle->dle_factory_addr, my_hardware_addr, 6);
  437.     bcopy(dle->dle_current_addr, my_hardware_addr, 6);
  438.  
  439. #endif
  440.  
  441. // DRF end
  442.  
  443.  
  444.     /*
  445.      * Allow the DLPI common code to initialize the dle fields. Once
  446.      * dle_init is called, dle_terminate must be called before freeing
  447.      * the dle structure.  There is private memory allocated for each
  448.      * dle that needs to be freed.
  449.      */
  450.     dle_init(dle, 0);
  451.     board_global = board;
  452.  
  453.     return true;
  454. }
  455.  
  456. void
  457. TerminateStreamModule (void)
  458. {
  459.     board_t    * board = board_global;
  460.  
  461.     loop_debug((trace_args "TerminateStreamModule" trace_eol));
  462.  
  463.     /*
  464.      * Suggestions:
  465.      * - Remove interrupt vectors.
  466.      * - Reset the hardware to a known state.
  467.      */
  468.  
  469.     board_global = (board_t *)NULL;
  470.     dle_terminate(&board->board_dle);
  471.     OTFreeMem(board);
  472. }
  473.  
  474. OTResult
  475. ValidateHardware (RegEntryID * our_id)
  476. {
  477.     #pragma unused(our_id)
  478.     loop_debug((trace_args "ValidateHardware" trace_eol));
  479.     return kOTNoError;    
  480. }
  481.  
  482. /* STREAMS close routine. */
  483. int loop_close (queue_t * q, int flag, cred_t * credp)
  484. {
  485.     #pragma unused(flag,credp)
  486.     loop_debug((trace_args "loop_close(%x), dcl_dle %x" trace_eol
  487.         , q, ((dcl_t *)q->q_ptr)->dcl_hw ));
  488.     /*
  489.      * NOTE: there is probably not much else that needs to be done
  490.      * in this routine.  If you need to know about the number of
  491.      * open instances, dle_refcount is the number of streams still
  492.      * referencing the device (decremented in dle_close).
  493.      */
  494.     return dle_close(q);
  495. }
  496.  
  497. void loop_hw_address_filter_reset (void * boardvp, dle_addr_t * addr_list
  498.             , ulong addr_count, ulong promisc_count
  499.             , ulong multi_promisc_count, ulong accept_broadcast
  500.             , ulong accept_error)
  501. {
  502.     #pragma unused(addr_count,accept_broadcast,accept_error)
  503.     board_t        * board = boardvp;
  504.     dle_t        * dle = &board->board_dle;
  505.     dle_addr_t    * dlea;
  506.     unsigned char    * first_phys_addr = 0;
  507.     uint        phys_addr_count = 0;
  508.  
  509.     /* Calculate the new logical address filter for multicast addresses. */
  510.     for (dlea = addr_list; dlea; dlea = dlea->dlea_next) {
  511.         if (dlea->dlea_addr[0] & 0x1) {
  512.             /*
  513.              * If the address is a multicast address, then set
  514.              * the right bits in the new filter.
  515.              */
  516.         } else {
  517.             /*
  518.              * Additional physical address.  This does not happen
  519.              * with the first version of the DLPI template code.
  520.              * However, at some future time, we may want to
  521.              * support multiple physical addresses on one
  522.              * hardware tap.
  523.              */
  524.             if (!first_phys_addr)
  525.                 first_phys_addr = dlea->dlea_addr;
  526.             phys_addr_count++;
  527.         }
  528.     }
  529.  
  530.     if (first_phys_addr) {
  531.         /*
  532.          * If there were any physical, non-multicast addresses in
  533.          * the list, then check to see if the first one is different
  534.          * from the current one.  If so, copy the new address into
  535.          * dle_current_addr and take whatever steps are necessary
  536.          * with the hardware to change the physical address.
  537.          */
  538.         ;
  539.     }
  540.  
  541.     /*
  542.      * Compare the new address filter with the old one.  If the new
  543.      * one is different, then set the hardware appropriately.
  544.      */
  545.  
  546.     /*
  547.      * If there are multiple physical addresses, then the board
  548.      * probably needs to be put in a promiscuous state.
  549.      */
  550.     if (phys_addr_count > 1)
  551.         promisc_count |= 1;
  552.  
  553.     if (promisc_count || multi_promisc_count)
  554.         /* Set the board into promiscuous mode. */;
  555.     else
  556.         /* Clear any promiscuous mode that may be set*/;
  557.     /*
  558.      * Save the promiscuous setting in the board structure so that
  559.      * updates to the hardware registers from loop_start or other
  560.      * routines will be correct.
  561.      */
  562. }
  563.  
  564. /* Called by dlpiether code through dlehw_start. */
  565. void loop_hw_start (void * boardvp)
  566. {
  567.     board_t    * board = boardvp;
  568.  
  569.     /* Kick the board alive and allow receive interrupts. */
  570. }
  571.  
  572. /* Called by dlpiether code through dlehw_stop. */
  573. void loop_hw_stop (void * boardvp)
  574. {
  575.     #pragma unused(boardvp)
  576.     /* Turn off interrupts and leave the hardware disabled. */
  577. }
  578.         
  579.  
  580. /* STREAMS open routine. */
  581. int loop_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp)
  582. {
  583.     int    ret_code;
  584.  
  585.     loop_debug((trace_args "loop_open()" trace_eol));
  586.     /*
  587.      * This routine probably does not need to do anything other
  588.      * than call dle_open.  dle_refcnt is incremented.
  589.      */
  590.     ret_code = dle_open(&board_global->board_dle, q, devp, flag, sflag, credp
  591.             , sizeof(loop_t));
  592.     loop_debug((trace_args "dle_open(%x) = %d, dcl_hw %x" trace_eol
  593.         , &board_global->board_dle
  594.         , ret_code
  595.         , ((dcl_t *)q->q_ptr)->dcl_hw ));
  596.     return ret_code;
  597. }
  598.  
  599.  
  600. /* 
  601.  * Read-side service routine.
  602.  * Pass all inbound packets upstream.  Messages are placed on
  603.  * the read-side queue by dle_inbound.  Unless the hardware
  604.  * requires something special, no additional code should be
  605.  * required.  NOTE: Additional messages may be added to the
  606.  * queue while this routine is running. If canputnext() fails,
  607.  * messages are freed rather than put back on the queue.  
  608.  * This is necessary since dle_inbound() will continue to add 
  609.  * messages without checking flow control (it can't call canputnext()
  610.  * from interrupt context).
  611.  */
  612. int loop_rsrv (q)
  613.     queue_t    * q;
  614. {
  615.     mblk_t * mp;
  616.  
  617.     while (mp = getq(q)) {
  618.         /*
  619.          * Private message to be passed back to the dlpiether
  620.          * code.  This interface is required for supporting
  621.          * 802.2 XID and Test packets.
  622.          */
  623.         if (mp->b_datap->db_type == M_CTL) {
  624.             dle_rsrv_ctl(q, mp);
  625.         } else if (canputnext(q))
  626.             putnext(q, mp);
  627.         else {
  628.             freemsg(mp);
  629.             flushq(q, FLUSHDATA);
  630.             break;
  631.         }
  632.     }
  633.     return 0;
  634. }
  635.  
  636. /* Write-side put routine. Only handles M_DATA, handing others to dlpiether. */
  637. int
  638. loop_wput (queue_t * q, mblk_t * mp)
  639. {
  640.     loop_t    * loop;
  641.     mblk_t    * first_mp;
  642.     long    remaining;
  643.     long    total;
  644.     unsigned char    * xmt_buf=nil;
  645.  
  646.     loop = (loop_t *)q->q_ptr;
  647.     if (mp->b_datap->db_type != M_DATA) {
  648.         mp = dle_wput(q, mp);
  649.         if (!mp)
  650.             return 0;
  651.         switch (mp->b_datap->db_type) {
  652.         case M_DATA:
  653.             break;        /* it's ready to send */
  654.         case M_IOCNAK:
  655.             /*
  656.              * Any driver private ioctl's come back from
  657.              * dle_wput() as an M_IOCNAK with ioc_error
  658.              * set to EINVAL; the rest of the original M_IOCTL
  659.              * is intact (including ioc_cmd & trailing M_DATAs).
  660.              * It may be processed here.
  661.              */
  662.              qreply(q,mp);
  663.              return 0;
  664.         default:
  665.             /* dle_wput() has formatted the reply for us */
  666.             qreply(q, mp);
  667.             return 0;
  668.         }
  669.     }
  670.  
  671.     /*
  672.      * Copy the packet to transmit buffer.  This is an example
  673.      * showing the copies and the calculation of the length
  674.      * of the packet.  Code for actual hardware devices will
  675.      * obviously need to be updated, at least to initialize
  676.      * xmt_buf to point to the hardware transmit area.
  677.      */
  678.     remaining = MAX_PACKET_SIZE;
  679.     first_mp = mp;
  680.  
  681.     do {
  682.         unsigned char    * rptr = mp->b_rptr;
  683.         int    len = mp->b_wptr - rptr;
  684.         if (len <= 0)
  685.             continue;
  686.         if (remaining < len) {
  687.             /* packet too large */
  688.             mp = dle_wput_ud_error(first_mp, DL_UNDELIVERABLE, 0);
  689.             if (mp)
  690.                 qreply(q, mp);
  691.             return 0;
  692.         }
  693.         remaining -= len;
  694.         xmt_buf += len;
  695.         board_bcopy_to_noncached(rptr, xmt_buf - len, len);
  696.     } while ((mp = mp->b_cont)  &&  remaining);
  697.  
  698.     total = MAX_PACKET_SIZE - remaining;
  699.  
  700.     /* Fill in the 802 length field if it is zero. */
  701.     {
  702.         unsigned char    * up;
  703.         up = &xmt_buf[-total];
  704.         if (!(up[12] | up[13])) 
  705.         {
  706.             uint    adjusted_total = total - 14;
  707.             up[12] = (unsigned char)(adjusted_total >> 8);
  708.             up[13] = (unsigned char)(adjusted_total & 0xff);
  709.         }
  710.     }
  711.  
  712.     if (total < MIN_PACKET_SIZE) 
  713.     {
  714.         /*
  715.          * If the packet is less than the minimum transmit
  716.          * size, then you need to pad the packet.  Hopefully
  717.          * this is just of matter of setting the hardware
  718.          * to pad automatically.
  719.          */
  720.          ;
  721.     }
  722.  
  723.     /* Trigger the hardware transmit. */
  724.     /* Increment the appropriate MIB interface statistics. */
  725.     ((dle_t *)loop->loop_dle)->dle_istatus.bytes_sent += total;
  726.     if (first_mp->b_rptr[0] & 0x1) {
  727.         unsigned char    * rptr = first_mp->b_rptr;
  728.         if ((rptr[0] & rptr[1] & rptr[2] & rptr[3] & rptr[4]
  729.              & rptr[5]) == 0xFF)
  730.             ((dle_t *)loop->loop_dle)->dle_istatus.broadcast_frames_sent++;
  731.         else
  732.             ((dle_t *)loop->loop_dle)->dle_istatus.multicast_frames_sent++;
  733.     } else
  734.         ((dle_t *)loop->loop_dle)->dle_istatus.unicast_frames_sent++;
  735.  
  736.     freemsg(first_mp);
  737.     return 0;
  738. }
  739.  
  740.  
  741.  
  742.